/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.suli.document.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.suli.document.domain.Tray;
import me.zhengjie.suli.document.repository.TrayRepository;
import me.zhengjie.suli.document.service.TrayService;
import me.zhengjie.suli.document.service.dto.TrayDto;
import me.zhengjie.suli.document.service.dto.TrayQueryCriteria;
import me.zhengjie.suli.document.service.mapStruct.TrayMapper;
import me.zhengjie.suli.inventory.domain.Inventory;
import me.zhengjie.suli.inventory.repository.InventoryRepository;
import me.zhengjie.suli.location.domain.Location;
import me.zhengjie.suli.location.repository.LocationRepository;
import me.zhengjie.suli.tasks.domain.Tasks;
import me.zhengjie.suli.tasks.repository.TasksRepository;
import me.zhengjie.suli.toHouse.domain.Receipt;
import me.zhengjie.suli.toHouse.domain.ReceiptDetail;
import me.zhengjie.suli.toHouse.repository.ReceiptDetailRepository;
import me.zhengjie.suli.toHouse.repository.ReceiptRepository;
import me.zhengjie.suli.wcs.domain.AgvTaskDetail;
import me.zhengjie.suli.wcs.domain.AgvTasks;
import me.zhengjie.suli.wcs.repository.AgvTaskDetailRepository;
import me.zhengjie.suli.wcs.repository.AgvTasksRepository;
import me.zhengjie.util.CommonUtil;
import me.zhengjie.util.UuidUtil;
import me.zhengjie.util.Variable;
import me.zhengjie.utils.FileUtil;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QueryHelp;
import me.zhengjie.utils.ValidationUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.persistence.Id;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Allen Gao
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2022-12-30
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class TrayServiceImpl implements TrayService {
    @Autowired
    private ReceiptDetailRepository receiptDetailRepository;
    @Autowired
    private ReceiptRepository receiptRepository;
    @Autowired
    private TasksRepository tasksRepository;
    @Autowired
    private AgvTaskDetailRepository agvTaskDetailRepository;
    @Autowired
    private AgvTasksRepository agvTasksRepository;
    private final TrayRepository trayRepository;
    private final TrayMapper trayMapper;
    private final InventoryRepository inventoryRepository;
    private final LocationRepository locationRepository;

    @Autowired
    CommonUtil commonUtil;

    @Value("${agv.port}")
    private String agvPort;

    @Value("${agv.ip}")
    private String agvIp;

    @Override
    public Map<String, Object> queryAll(TrayQueryCriteria criteria, Pageable pageable) {
        Page<Tray> page = trayRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(trayMapper::toDto));
    }

    @Override
    public List<TrayDto> queryAll(TrayQueryCriteria criteria) {
        return trayMapper.toDto(trayRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public TrayDto findById(String id) {
        Tray tray = trayRepository.findById(id).orElseGet(Tray::new);
        ValidationUtil.isNull(tray.getId(), "Tray", "id", id);
        return trayMapper.toDto(tray);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TrayDto create(Tray resources) {
        resources.setId(IdUtil.simpleUUID());
        return trayMapper.toDto(trayRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Tray resources) {
        Tray tray = trayRepository.findById(resources.getId()).orElseGet(Tray::new);
        ValidationUtil.isNull(tray.getId(), "Tray", "id", resources.getId());
        tray.copy(resources);
        trayRepository.save(tray);
    }

    @Override
    public void deleteAll(String[] ids) {
        for (String id : ids) {
            trayRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<TrayDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (TrayDto tray : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("托盘编码", tray.getTrayCode());
            map.put("托盘名称", tray.getTrayName());
            map.put("托盘状态", tray.getTrayStatus());
            map.put("托盘类型", tray.getTrayType());
            map.put("所属巷道", tray.getRoadWay());
            map.put("备注", tray.getRemark());
            map.put("创建用户", tray.getCreateBy());
            map.put("修改用户", tray.getUpdateBy());
            map.put("创建日期", tray.getCreateTime());
            map.put("修改日期", tray.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int callEmptyBox(String param) {
        JSONObject jsonObject = JSON.parseObject(param);
        int type = jsonObject.getIntValue("type");
        int qty = jsonObject.getIntValue("qty");    // 取空料箱的数量
        String receiptCode = jsonObject.getString("receiptCode");
        String materialCode = jsonObject.getString("materialCode");

        if (qty == 0) {
            Receipt receipt = receiptRepository.getReceiptByReceiptCode(receiptCode);
            List<ReceiptDetail> receiptDetailList = receiptDetailRepository.getReceiptDetailsById(receipt.getId());
            qty = receiptDetailList.size();
        }

        List<Location> locationList = new ArrayList<>();    // 需要拿取料箱的库位
        /** step 1: 检查该物料在4层及以下有没有足够的库存 */
        List<Inventory> inventoryList = inventoryRepository.getInventoryByMaterialCodeWithLayerLessThen4(materialCode);
        if (inventoryList.size() < 4) {    // 如果在小于4层库存不足，则需要把小于四层的库存补足
            int needQtyLessThen4 = 4 - inventoryList.size();    // 4层一下取药补足的空料箱数量

            if (qty <= needQtyLessThen4) { // 如果任务数量小于需补足的箱数，直接取任务数量，
                locationList = locationRepository.getLocationsByQtyAndTypeWithLayerLessThen4(qty, type);
                /** 判断此时1，2，3，4层的空料箱能不能满足任务数量 */
                if (locationList.size() < qty) {    // 如果4层一下的空料箱数量不能满足任务数量，需要再取
                    int remainQty = qty - locationList.size();
                    List<Location> locationListRemainQty = locationRepository.getLocationsByQtyAndTypeOrderByLayerDesc(remainQty, type);
                    for (Location location : locationListRemainQty) {
                        location.setStatus(2);  // 2--禁用库位，防止重复选取
                        locationRepository.save(location);
                        locationList.add(location);
                    }
                } else {    // 如果能满足
                    for (Location location : locationList) {
                        location.setStatus(2);  // 2--禁用库位
                        locationRepository.save(location);
                    }
                }
            } else { // 如果任务数量大于需补足的数量，只取需补足的箱数
                List<Location> locationListBelow4 = locationRepository.getLocationsByQtyAndTypeWithLayerLessThen4(needQtyLessThen4, type);
                for (Location location : locationListBelow4) {
                    locationList.add(location); // 把准备拿料箱的库位加入到库位List
                    /** 禁用库位，避免重复选中 */
                    location.setStatus(2);  // 2--禁用库位
                    locationRepository.save(location);
                }

                /** 剩余的箱数，从高往下排序 */
                int remainQty = qty - locationListBelow4.size();    // qty-locationListBelow2.size() 防止1，2，3，4 层空库位不够，需要从其他层补. 卧槽，我真聪明！！！！
                List<Location> locationListRemainQty = locationRepository.getLocationsByQtyAndTypeOrderByLayerDesc(remainQty, type);
                for (Location location : locationListRemainQty) {
                    location.setStatus(2);  // 2--禁用库位，防止重复选取
                    locationRepository.save(location);
                    locationList.add(location);
                }
            }
        } else { // 直接按顺序取(从高到低)
            locationList = locationRepository.getLocationsByQtyAndTypeOrderByLayerDesc(qty, type);
            for (Location location : locationList) {
                location.setStatus(2);  // 2--禁用库位
                locationRepository.save(location);
            }
        }

        if (locationList.size() != qty) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return 2;   // 空料箱不足
        }

        /** 生成AGV任务 */
        JSONObject jsonObject1 = new JSONObject();
        jsonObject1.put("taskType", "tote_outbound");
        jsonObject1.put("taskGroupCode", UuidUtil.genCode());
        jsonObject1.put("groupPriority", 5);
        JSONArray jsonArray = new JSONArray();
        int v = 1;
        Snowflake snowflake = new Snowflake();
        for (Location location : locationList) {
            JSONObject jsonObject2 = new JSONObject();
            jsonObject2.put("taskCode", UuidUtil.genCode());
            jsonObject2.put("taskPriority", v);
            v = v + 1;
            /** 存入AGV任务表中 */
            AgvTasks agvTasks1 = new AgvTasks();
            agvTasks1.setId(snowflake.nextIdStr());
            agvTasks1.setBillNumber(jsonObject2.getString("taskCode"));
            agvTasks1.setTaskType("空料箱出库");
            agvTasks1.setStatus(1);
            agvTasksRepository.save(agvTasks1);
            JSONObject jsonObject3 = new JSONObject();
            jsonObject3.put("containerCode", location.getTrayCode()); // 料箱号
//            jsonObject3.put("containerType", "CT_KUBOT_STANDARD"); // 料箱类型
            jsonObject3.put("storageTag", "");   // 留空
            List<String> list = new ArrayList<>();
            list.add("CY02");
            jsonObject3.put("containerType", "");
            jsonObject3.put("fromLocationCode", location.getLocationCode());    // 库位号
            jsonObject3.put("toStationCode", list);   // 出库的目标操作台编号，协商固定值
//            jsonObject3.put("toLocationCode", "");  // 留空

            jsonObject2.put("taskDescribe", jsonObject3);
            jsonArray.add(jsonObject2);
            /** 生成空料箱出库任务 */
            Tasks tasks = new Tasks();
            tasks.setId(IdUtil.simpleUUID());
            tasks.setTaskCode(UuidUtil.genCode());
            tasks.setStatus(0); // 任务状态 0--未开始
            tasks.setLocationFrom(location.getLocationCode());
            tasks.setLocationTo("CY02");
            tasks.setTrayCode(location.getTrayCode());
            tasks.setTaskType(10); // 10--料箱出库
            tasks.setElName(Variable.emptyTrayName);
            tasks.setElUnit(Variable.emptyTrayUnit);
            tasks.setMtQty(new BigDecimal(1));
            tasks.setSourceCode(receiptCode);
            tasksRepository.save(tasks);

            /** 生成AGVTaskDetail */
            AgvTaskDetail agvTaskDetail = new AgvTaskDetail();
            agvTaskDetail.setId(IdUtil.simpleUUID());
            agvTaskDetail.setStatus(0);
            agvTaskDetail.setTaskCode(UuidUtil.genCode());
            agvTaskDetail.setTaskPriority(0);
            agvTaskDetail.setContainerCode("");
            agvTaskDetail.setContainerType("CT_KUBOT_STANDARD");
            agvTaskDetail.setStorageTag("");
            agvTaskDetail.setFromLocationCode(location.getLocationCode());
            agvTaskDetail.setToStationCode("CY02");
            agvTaskDetail.setToLocationCode("");
            agvTaskDetail.setMainId(agvTasks1.getId());
            agvTaskDetail.setSourceCode(tasks.getTaskCode());
            agvTaskDetailRepository.save(agvTaskDetail);
        }

        jsonObject1.put("tasks", jsonArray);
        String url = "http://" + agvIp + ":" + agvPort + "/task/create";
        log.info("呼叫空料箱，发送给AGV, " + jsonObject1);
        String resp = commonUtil.httpPost(url, jsonObject1);
        log.info("呼叫空料箱，接收到AGV反馈, " + resp);


        return 1;   //
    }
}